package com.itlong.cloud.intercept;

import brave.Tracing;
import brave.propagation.B3Propagation;
import brave.propagation.ExtraFieldPropagation;
import com.itlong.cloud.PlatformConstants;
import com.itlong.cloud.annotation.LogFeatures;
import org.apache.commons.lang.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.MDC;
import org.springframework.cloud.sleuth.instrument.web.TraceWebServletAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.nio.charset.StandardCharsets;
import java.util.Base64;

/**
 * <desc>
 * 日志服务功能打印
 * </desc>
 *
 * @createDate 2019/09/18
 */
@Aspect
@Component
@Order(10)
public class LogFeaturesInterceptor {

    // 定义一个切入点
    @Pointcut("execution(* com.itlong.cloud.controller.nonstandard.SmokeAlarmController.*(..))")
    public void execute() {
    }

    /**
     * <desc>
     * 日志中统一打印功能注解
     * </desc>
     *
     * @param pjp
     * @param logFeatures
     * @return java.lang.Object
     * @author maju
     * @createDate 2021/5/21
     */

    @Around("@annotation(logFeatures)")
    public Object around(ProceedingJoinPoint pjp, LogFeatures logFeatures) throws Throwable {
        MDC.put(PlatformConstants.FEATURES, logFeatures.value());
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);
        request.setAttribute(PlatformConstants.FEATURES, logFeatures.value());
        return pjp.proceed();
    }

    /**
     * <desc>
     * 日志中统一打印请求ID和功能
     * </desc>
     *
     * @param pjp
     * @return java.lang.Object
     * @author maju
     * @createDate 2021/5/21
     */

    @Around("execute()")
    public Object setRequestId(ProceedingJoinPoint pjp) throws Throwable {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        request.setCharacterEncoding("UTF-8");
        //尝试从request中获取参数
        Object requestId = request.getAttribute("requestId");
        Object features = request.getAttribute(PlatformConstants.FEATURES);
        String featuresStr = null;
        //尝试从sleuth中获取参数
        if (null == features) {
            featuresStr = ExtraFieldPropagation.get(PlatformConstants.FEATURES);
        } else {
            featuresStr = features.toString();
        }
        //中文会乱码 先取出解码 放入日志打印 加码在传递到下一微服务
        if (null != featuresStr) {
            //从sleuth中获取的要先解码
            if (features == null) {
                featuresStr = new String(Base64.getDecoder().decode(featuresStr), "utf-8");
            }
            ExtraFieldPropagation.set(PlatformConstants.FEATURES, Base64.getEncoder().encodeToString(featuresStr.getBytes("utf-8")));
            MDC.put(PlatformConstants.FEATURES, featuresStr);
        }
        if (null == requestId) {
            requestId = request.getParameter("requestId");
        }
        //尝试从sleuth中获取参数
        if (null == requestId) {
            requestId = ExtraFieldPropagation.get(PlatformConstants.REQUEST_ID);
        }
        if (null != requestId && StringUtils.isNotEmpty(requestId.toString())) {
            ExtraFieldPropagation.set(PlatformConstants.REQUEST_ID, new String(requestId.toString().getBytes(StandardCharsets.UTF_8)));
            MDC.put(PlatformConstants.REQUEST_ID, requestId.toString());
        }
        return pjp.proceed();
    }

    /**
     * 定义链路追踪额外传播字段
     *
     * @return
     */
    @Bean
    @Order(TraceWebServletAutoConfiguration.TRACING_FILTER_ORDER - 2)
    public Tracing tracing() {
        return Tracing.newBuilder().propagationFactory(
                ExtraFieldPropagation.newFactory(B3Propagation.FACTORY, PlatformConstants.REQUEST_ID, PlatformConstants.FEATURES)
        ).build();
    }
}
